<?php

/**
 * Pwiff - PHP SWF Library
 *
 * LICENSE
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 *
 * @package    Pwiff
 * @subpackage Records
 * @copyright  Copyright (c) 2010 Steven Harris
 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @version    $Id: CXForm.php 5 2010-02-14 05:08:39Z indigo.sunrise $
 */

/**
 * Represents a color transform record.
 *
 * @package    Pwiff
 * @subpackage Records
 */
class Pwiff_Record_CXForm extends Pwiff_Record
{
	/**
	 * Red multiplication term.
	 *
	 * @var float
	 */
	protected $redMultTerm = null;
	
	/**
	 * Green multiplication term.
	 *
	 * @var float
	 */
	protected $greenMultTerm = null;
	
	/**
	 * Blue multiplication term.
	 *
	 * @var float
	 */
	protected $blueMultTerm = null;
	
	/**
	 * Red addition term.
	 *
	 * @var int
	 */
	protected $redAddTerm = null;
	
	/**
	 * Green addition term.
	 *
	 * @var int
	 */
	protected $greenAddTerm = null;
	
	/**
	 * Blue addition term.
	 *
	 * @var int
	 */
	protected $blueAddTerm = null;
	
	/**
	 * Sets the red multiplication term.
	 *
	 * @param  float $redMultTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setRedMultTerm($redMultTerm)
	{
		if ($redMultTerm !== null) {
			$this->verifyTerm($redMultTerm * 256);
		}
		$this->redMultTerm = $redMultTerm;
		return $this;
	}
	
	/**
	 * Sets the green multiplication term.
	 * 
	 * @param  float $greenMultTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setGreenMultTerm($greenMultTerm)
	{
		if ($greenMultTerm !== null) {
			$this->verifyTerm($greenMultTerm * 256);
		}
		$this->greenMultTerm = $greenMultTerm;
		return $this;
	}
	
	/**
	 * Sets the blue multiplication term.
	 *
	 * @param  float $blueMultTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setBlueMultTerm($blueMultTerm)
	{
		if ($blueMultTerm !== null) {
			$this->verifyTerm($blueMultTerm * 256);
		}
		$this->blueMultTerm = $blueMultTerm;
		return $this;
	}
	
	/**
	 * Sets the red addition term.
	 *
	 * @param  int $redAddTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setRedAddTerm($redAddTerm)
	{
		$this->verifyTerm($redAddTerm);
		$this->redAddTerm = $redAddTerm;
		return $this;
	}
	
	/**
	 * Sets the green addition term.
	 * 
	 * @param  int $greenAddTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setGreenAddTerm($greenAddTerm)
	{
		$this->verifyTerm($greenAddTerm);
		$this->greenAddTerm = $greenAddTerm;
		return $this;
	}
	
	/**
	 * Sets the blue addition term.
	 * 
	 * @param  int $blueAddTerm
	 * @return Pwiff_Record_CXForm
	 */
	public function setBlueAddTerm($blueAddTerm)
	{
		$this->verifyTerm($blueAddTerm);
		$this->blueAddTerm = $blueAddTerm;
		return $this;
	}
	
	/**
	 * Gets the red multiplication term, or null if not set.
	 *
	 * @return float
	 */
	public function getRedMultTerm()
	{
		return $this->redMultTerm;
	}
	
	/**
	 * Gets the green multiplication term, or null if not set.
	 * 
	 * @return float
	 */
	public function getGreenMultTerm()
	{
		return $this->greenMultTerm;
	}
	
	/**
	 * Gets the blue multiplication term, or null if not set.
	 * 
	 * @return float
	 */
	public function getBlueMultTerm()
	{
		return $this->blueMultTerm;
	}
	
	/**
	 * Gets the red addition term, or null if not set.
	 * 
	 * @return int
	 */
	public function getRedAddTerm()
	{
		return $this->redAddTerm;
	}
	
	/**
	 * Gets the green addition term, or null if not set.
	 *
	 * @return int
	 */
	public function getGreenAddTerm()
	{
		return $this->greenAddTerm;
	}
	
	/**
	 * Gets the blue addition term, or null if not set.
	 * 
	 * @return int
	 */
	public function getBlueAddTerm()
	{
		return $this->blueAddTerm;
	}
	
	/**
	 * Gets the length of this record.
	 *
	 * @return int
	 */
	public function getRecordLength()
	{
		// 6 bits to start
		$bits    = 6;
		$numBits = $this->getBitsNeeded();
		
		// for each term type, there are 3 SB[numBits]
		if ($this->hasMultTerms()) {
			$bits += $numBits * 3;
		}
		if ($this->hasAddTerms()) {
			$bits += $numBits * 3;
		}
		
		return ceil($bits / 8); // round up to full byte
	}
	
	/**
	 * Writes the color transform values to the given output instance.
	 *
	 * @param  Pwiff_Output $out
	 */
	public function write(Pwiff_Output $out)
	{
		$out->byteAlign();
		$out->writeBits($this->hasAddTerms()  ? 1 : 0, 1);
		$out->writeBits($this->hasMultTerms() ? 1 : 0, 1);
		
		$numBits = $this->getBitsNeeded();
		$out->writeBits($numBits, 4);
		
		// multiplication terms
		if ($this->hasMultTerms()) {
			$this->writeMultTerms($out, $numBits);
		}
		
		// addition terms
		if ($this->hasAddTerms()) {
			$this->writeAddTerms($out, $numBits);
		}
	}
	
	/**
	 * Writes the multiplication terms to the given output instance.
	 * 
	 * @param  Pwiff_Output $out
	 * @param  int $numBits
	 */
	protected function writeMultTerms(Pwiff_Output $out, $numBits)
	{
		$out->writeBits((int) (256 * ((float) $this->redMultTerm)),   $numBits);
		$out->writeBits((int) (256 * ((float) $this->greenMultTerm)), $numBits);
		$out->writeBits((int) (256 * ((float) $this->blueMultTerm)),  $numBits);
	}
	
	/**
	 * Writes the addition terms to the given output instance.
	 * 
	 * @param  Pwiff_Output $out
	 * @param  int $numBits
	 */
	protected function writeAddTerms(Pwiff_Output $out, $numBits)
	{
		$out->writeBits((int) $this->redAddTerm,   $numBits);
		$out->writeBits((int) $this->greenAddTerm, $numBits);
		$out->writeBits((int) $this->blueAddTerm,  $numBits);
	}
	
	/**
	 * Returns true if the record has any multiplication terms.
	 *
	 * @return bool
	 */
	protected function hasMultTerms()
	{
		if ($this->redMultTerm === null AND $this->greenMultTerm === null
			AND $this->blueMultTerm === null) {
			return false;
		}
		return true;
	}
	
	/**
	 * Returns true if the record has any addition terms.
	 *
	 * @return bool
	 */
	protected function hasAddTerms()
	{
		if ($this->redAddTerm === null AND $this->greenAddTerm === null
			AND $this->blueAddTerm === null) {
			return false;
		}
		return true;
	}
	
	/**
	 * Returns the number of bits needed to represent the CXFORM's values.
	 *
	 * @return int
	 */
	protected function getBitsNeeded()
	{
		$numbers = array();
		
		if ($this->hasAddTerms()) {
			$numbers[] = (int) $this->redAddTerm;
			$numbers[] = (int) $this->greenAddTerm;
			$numbers[] = (int) $this->blueAddTerm;
		}
		
		if ($this->hasMultTerms()) {
			$numbers[] = (int) (256 * ((float) $this->redMultTerm));
			$numbers[] = (int) (256 * ((float) $this->greenMultTerm));
			$numbers[] = (int) (256 * ((float) $this->blueMultTerm));
		}
		
		return Pwiff_Util_Numerics::getSignedBitCount($numbers);
	}
	
	/**
	 * Verifies that the given term can fit within 15 signed bits.
	 *
	 * @param  int $term
	 */
	protected function verifyTerm($term)
	{
		if ($term !== null AND !Pwiff_Util_Numerics::canFitSigned((int) $term, 15)) {
			throw new Pwiff_Exception('Term out of range.');
		}
	}
}
